/**
 *\copyright Copyright(C),2024, Ningbo Tuopu Group Co., Ltd.
 *\file      DebugTrace.c
 *\date      2025-04-12 00:00:00
 *\author    Chuhan Zheng
 *\version   1.0
 *\brief     Description:
 */

/****************************************************************************/
/*                             include area                                 */
/****************************************************************************/
#include "DebugTrace.h"
// #include "McalIf_Uart.h"

/****************************************************************************/
/*                             local data at ROM                            */
/****************************************************************************/
static inline const char *DebugTrace_LogLevelToString(DebugTrace_LogLevel level);
static inline void DebugTrace_ApplyTruncation(void);
static inline void DebugTrace_Transmit(const char *buffer, int length);

/****************************************************************************/
/*                             definition area                              */
/****************************************************************************/
#define DEBUGTRACE_LINEBREAKSLENGTH (1U)
#define DEBUG_TRACE(buffer_ptr, buffer_len) DebugTrace_Transmit(buffer_ptr, buffer_len)

/****************************************************************************/
/*                             Typedef area                                 */
/****************************************************************************/

/****************************************************************************/
/*                             local data at RAM                            */
/****************************************************************************/
static volatile uint8_t DebugTrace_SyncFlag = (uint8_t)DEBUGTRACE_TYPE_TRUE;

/****************************************************************************/
/*                             global data at RAM                           */
/****************************************************************************/
char DebugTrace_Buffer[DEBUGTRACE_BUFFER_SIZE] = {0x00};
int DebugTrace_MaximumBufferUsage = 0U;

/****************************************************************************/
/*                             global data at ROM                           */
/****************************************************************************/

/****************************************************************************/
/*                        Local function declare area                       */
/****************************************************************************/
/* Auxiliary function: Convert enumeration to string */
static inline const char *DebugTrace_LogLevelToString(DebugTrace_LogLevel level)
{
  switch (level)
  {
  case LOG_LEVEL_DEBUG:
    return "DEBUG";
  case LOG_LEVEL_INFO:
    return " INFO";
  case LOG_LEVEL_WARN:
    return " WARN";
  case LOG_LEVEL_ERROR:
    return "ERROR";
  default:
    return "UNKWN";
  }
}

/**
 * \brief Helper function to apply truncation marker "...\n" at the end of buffer
 * \note  This function assumes DebugTrace_Buffer is global
 */
static inline void DebugTrace_ApplyTruncation(void)
{
  /* Insufficient buffer, information truncated */
  /* You can choose to add a truncation marker at the end of the buffer, such as "...\n" */
  const char truncation_marker[] = "...\n";
  /* Calculate the length including '\0' */
  int marker_length = sizeof(truncation_marker);

  if (DEBUGTRACE_BUFFER_SIZE > marker_length)
  {
    /* Firstly, ensure that the original string terminates correctl */
    strncpy(DebugTrace_Buffer + DEBUGTRACE_BUFFER_SIZE - marker_length, truncation_marker, marker_length);
  }
  else
  {
    /* If the buffer is too small to fit even '...' and '0',
    do the safest thing possible, such as placing '0' at the last position */
    if (DEBUGTRACE_BUFFER_SIZE > 0)
    {
      DebugTrace_Buffer[DEBUGTRACE_BUFFER_SIZE - 1] = '\0';
    }
  }
}

/**
 * \brief The final log execution function
 * \param buffer Pointer to log data (already included).
 * \param length The exact length of the data to be sent.
 */
static inline void DebugTrace_Transmit(const char *buffer, int length)
{
  printf("%s", buffer);

  // /* Waiting for the completion of the previous data transmission */
  // if (DebugTrace_SyncFlag == (uint8_t)DEBUGTRACE_TYPE_TRUE)
  // {
  //   while (UART->DMA_TX_LENGTH.reg != 0u)
  //   {
  //     /* wait transmit */
  //   }
  // }
  // McalIf_Uart_Tx_Date((uint16_t)buffer, (uint16_t)length);
  // /*Waiting for the completion of this data transmission*/
  // if (DebugTrace_SyncFlag == (uint8_t)DEBUGTRACE_TYPE_TRUE)
  // {
  //   while (UART->DMA_TX_LENGTH.reg != 0u)
  //   {
  //     /* wait transmit */
  //   }
  // }
}

/****************************************************************************/
/*                        Global function declare area                      */
/****************************************************************************/

/**
 * \brief DebugTrace Packaging
 * \details 1. Simulate dynamic "psn" using the static variable d_psn_counter
 *          2. Convert level enumeration using DebugTrace_LogLevelToString()
 * \param level       : Log level (LOG_LEVEL_INFO, etc.)
 * \param core_name : Core name (such as "Acore", "Bcore")
 * \return none
 * \note none
 */
void DebugTrace_Packaging(DebugTrace_LogLevel level, const char *core_name, uint32_t combined_log_id, const char *fmt, int args_count, ...)
{
  /* 1. Define local log buffer */
  int header_length = 0;
  int total_length = 0;

  /* 2. Simulate dynamic PSN */
  static uint32_t g_psn_counter = 1;
  uint32_t current_psn = g_psn_counter++;

  /* 3. Build log headers using snprintf */
  /* Snprintf is secure as it prevents buffer overflow */
  /* It returns the actual number of characters written (excluding the last \0) */
  header_length = snprintf(DebugTrace_Buffer, DEBUGTRACE_BUFFER_SIZE,
                           "[%s][%s][psn:%u][0x%x]:",
                           DebugTrace_LogLevelToString(level),
                           core_name,
                           current_psn,
                           combined_log_id);

  /* Check if snprintf has failed or if the buffer is too small */
  if (header_length < 0 || header_length >= DEBUGTRACE_BUFFER_SIZE)
  {
    /* Buffer too small or error, unable to continue */
    const char *errMsg = "[ERROR][DEBUGTRACE]Out of buffer\n";
    int chars_written = snprintf(DebugTrace_Buffer, DEBUGTRACE_BUFFER_SIZE, "%s", errMsg);

    if (chars_written >= DEBUGTRACE_BUFFER_SIZE)
    {
      /* Use helper function to handle truncation */
      DebugTrace_ApplyTruncation();
      total_length = DEBUGTRACE_BUFFER_SIZE - 1;
    }
    else
    {
      total_length = chars_written;
    }
  }
  else
  {
    /* 4. Attach log body to buffer using vsnprintf */
    /* (log_fuffer+header_length): Starting from the end of the log header */
    /* (LOG_SUFFER_SIZE - header_length): Remaining space in the buffer */
    va_list args;
    va_start(args, args_count);
    vsnprintf(DebugTrace_Buffer + header_length,      /* destination address */
              DEBUGTRACE_BUFFER_SIZE - header_length, /* remaining space */
              fmt,                                    /* Format string */
              args);                                  /* parameter list */
    va_end(args);

    /* Vsnprintf will automatically add \ 0 at log_fuffer+header_length */
    /* Even if the content exceeds the remaining space, it will only be truncated and will not overflow */

    /* Obtain the actual string length */
    total_length = strlen(DebugTrace_Buffer);

    /* Check if line breaks and end characters can be added */
    if (total_length < (DEBUGTRACE_BUFFER_SIZE - DEBUGTRACE_LINEBREAKSLENGTH))
    {
      /* There is space in the buffer zone */
      DebugTrace_Buffer[total_length] = '\n';      /* add line breaks \n */
      DebugTrace_Buffer[total_length + 1] = '\0';  /* Ensure a new null terminator */
      total_length += DEBUGTRACE_LINEBREAKSLENGTH; /* The length only increases by one characters, \n */
    }
    else
    {
      /* Buffer is full, use helper function to handle truncation */
      DebugTrace_ApplyTruncation();
      total_length = DEBUGTRACE_BUFFER_SIZE - 1;
    }
  }

  /* Statistics on the usage status of the maximum buffer zone */
  if (DebugTrace_MaximumBufferUsage < total_length)
  {
    DebugTrace_MaximumBufferUsage = total_length + DEBUGTRACE_LINEBREAKSLENGTH;
  }

  /* Log printing: Pass the final buffer and length to the transport layer */
  DEBUG_TRACE(DebugTrace_Buffer, total_length);
}

/**
 * \brief DebugTrace Sync Transmit Enable
 */
void DebugTrace_SyncTransmitEnable(void)
{
  DebugTrace_SyncFlag = (uint8_t)DEBUGTRACE_TYPE_TRUE;
}

/**
 * \brief DebugTrace ASync Transmit Enable
 */
void DebugTrace_ASyncTransmitEnable(void)
{
  DebugTrace_SyncFlag = (uint8_t)DEBUGTRACE_TYPE_FALSE;
}

/****************************************************************************/
/*                       Example function declare area                      */
/****************************************************************************/

/**
 * \brief DebugTrace Example
 */
void DebugTrace_Example(void)
{
  /* Demo IPC LOG Template */
  /* --- Demonstrate dynamic IPC header information --- */
  ACORE_IPC_LOG_DEBUG(
      LOG_IPC_BT_APP,  /* log_id */
      "UXADP",         /* ipc_type */
      "BT->APP",       /* ipc_direction */
      0x84, 0x04, 4, 6 /* service, cmd, len, ts */
  );

  BCORE_IPC_LOG_DEBUG(
      LOG_IPC_APP_BT,   /* log_id */
      "CMD_Q",          /* ipc_type */
      "APP->CORE",      /* ipc_direction */
      0x01, 0x0a, 8, 10 /* service, cmd, len, ts */
  );

  /* Demo MODULE LOG Template */
  /* "--- Demo dynamic log header --- */
  MINIOS_APP_PRINT_INFO(
      11,                                         /* log code */
      "[minios_app_notification_start_callback]", /* printf string */
      DEBUGTRACE_NO_ARG);                         /* no param */

  MINIOS_APP_PRINT_INFO(
      12,                                        /* log code */
      "[minios_app_notification_stop_callback]", /* printf string */
      DEBUGTRACE_NO_ARG);                        /* no param */

  int read_len = 80;
  MINIOS_APP_PRINT_INFO(
      13,                                       /* log code */
      "[minios_app_data_callback] read_len:%d", /* printf string */
      1,                                        /* 1 params */
      read_len);                                /* param 1 */

  char *state = "failed";
  int error_code = -12;
  MINIOS_APP_PRINT_ERROR(
      401,                                        /* log code */
      "[minios_app_init] Init %s! error_code=%d", /* printf string */
      2,                                          /* 2 params */
      state,                                      /* param 1 */
      error_code);                                /* param 2 */
}
